<?php
// +----------------------------------------------------------------------
// | INPHP
// +----------------------------------------------------------------------
// | Copyright (c) 2021 https://inphp.cc All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://opensource.org/licenses/MIT )
// +----------------------------------------------------------------------
// | Author: lulanyin <me@lanyin.lu>
// +----------------------------------------------------------------------
namespace Inphp;

use Inphp\Util\File;

/**
 * 缓存类
 * Class Cache
 * @package Inphp
 */
class Cache
{
    /**
     * 获取缓存
     * @param string $name
     * @param mixed $default
     * @param string $name_prefix
     * @return mixed
     */
    public static function get(string $name, mixed $default = null, string $name_prefix = "") : mixed
    {
        $config = Config::get('service.cache');
        if($config['driver'] == 'middleware'){
            return self::processMiddleware("get", $name, $default, $name_prefix);
        }else{
            $cache_path = $config['path'];
            if(is_dir($cache_path)){
                $uri = $cache_path."/".$name;
                if(is_file($uri)){
                    $data = file_get_contents($uri);
                    if($item = json_decode($data, true))
                    {
                        $expire = $item['expire'];
                        if($expire > time() || $expire == 0)
                        {
                            return $item['data'] ?? $default;
                        }
                    }
                }
            }
        }
        return $default;
    }

    /**
     * 设置缓存
     * @param string $name
     * @param mixed $value
     * @param int|null $time
     * @param string $name_prefix
     */
    public static function set(string $name, mixed $value, int|null $time = null, string $name_prefix = "") : void
    {
        $config = Config::get('service.cache');
        $time = is_int($time) && $time > 0 ? $time : $config['time'];
        $cache_path = $config['path'];
        if($config['driver'] == 'middleware'){
            self::processMiddleware("set", $name, $value, $time, $name_prefix);
        }else{
            if(!is_dir($cache_path)){
                @mkdir($cache_path, 0777, true);
            }
            if(is_dir($cache_path)){
                $uri = $cache_path."/".$name;
                $data = [
                    "expire"    => $time == 0 ? 0 : (time() + $time),
                    "time"      => time(),
                    "data"      => $value
                ];
                file_put_contents($uri, json_encode($data, JSON_UNESCAPED_UNICODE));
                //同时，保存一个过期时间
                self::setExpireTime($cache_path, $name, $data['expire']);
            }
        }
    }
    
    /**
     * 保存每个缓存的过期时间
     * @param string $cache_path
     * @param string $name
     * @param int $expire_time
     */
    private static function setExpireTime(string $cache_path, string $name, int $expire_time = 0): void
    {
        if($expire_time > 0)
        {
            $list = [];
            $uri = $cache_path."/00000_expire_list";
            if(is_file($uri)){
                $data = file_get_contents($uri);
                $list = json_decode($data, true) ?? [];
            }
            $list[$name] = $expire_time;
            file_put_contents($uri, json_encode($list, JSON_UNESCAPED_UNICODE));
        }
    }
    
    /**
     * 清除已过期的本地文件缓存
     */
    public static function cleanExpireCache(): void
    {
        $config = Config::get('service.cache');
        if($config['driver'] !== 'middleware'){
            $cache_path = $config['path'];
            $uri = $cache_path."/00000_expire_list";
            if(is_file($uri)){
                $data = file_get_contents($uri);
                $list = json_decode($data, true) ?? [];
                if(!empty($list))
                {
                    $now = time();
                    $new_list = [];
                    $has_remove = false;
                    foreach ($list as $key => $expire_time)
                    {
                        if($expire_time <= $now)
                        {
                            self::remove($key, $cache_path);
                            $has_remove = true;
                        }
                        else
                        {
                            $new_list[$key] = $expire_time;
                        }
                    }
                    if($has_remove)
                    {
                        //重新保存
                        if(!empty($new_list))
                        {
                            file_put_contents($uri, json_encode($new_list, JSON_UNESCAPED_UNICODE));
                        }
                        else
                        {
                            @unlink($uri);
                        }
                    }
                }
            }
        }
    }

    /**
     * 更新数组类数据的某个键值
     * @param string $name
     * @param array $values
     * @param int|null $time
     * @param string $name_prefix
     */
    public static function update(string $name, array $values = [], int|null $time = null, string $name_prefix = "") : void
    {
        $config = Config::get('service.cache');
        if($config['driver'] == 'middleware'){
            $data = self::processMiddleware("get", $name, $name_prefix);
            $data = is_array($data) ? $data : [];
            $data = array_merge($data, $values);
            self::set($name, $data, $time, $name_prefix);
        }else{
            $data = self::get($name);
            $data = is_array($data) ? $data : [];
            $data = array_merge($data, $values);
            $time = is_int($time) && $time > 0 ? $time : $config['time'];
            self::set($name, $data, $time, $name_prefix);
        }
    }

    /**
     * 删除缓存
     * @param string $name
     * @param string $name_prefix
     */
    public static function remove(string $name, string $name_prefix = "") : void
    {
        $config = Config::get('service.cache');
        if($config['driver'] == 'middleware'){
            self::processMiddleware("remove", $name, $name_prefix);
        }else{
            $cache_path = $config['path'];
            if(is_dir($cache_path)){
                $uri = $cache_path."/".$name;
                if(is_file($uri)){
                    @unlink($uri);
                }
            }
        }
    }

    /**
     * 清除
     */
    public static function clean(string|null $name_prefix = null){
        $config = Config::get('service.cache');
        if($config['driver'] == 'middleware'){
            self::processMiddleware("clean", $name_prefix);
        }else{
            $cache_path = $cache_path ?? $config['path'];
            File::clearDir($cache_path);
        }
    }

    /**
     * 统一中间键
     * @param string $method
     * @param string|null $name
     * @param mixed|null $value
     * @param mixed $time
     * @param string|null $name_prefix 兼容redis的自定义前缀
     * @return mixed
     */
    private static function processMiddleware(string $method, string|null $name = null, mixed $value = null, mixed $time = 86400, string|null $name_prefix = null) : mixed
    {
        $middlewares = Config::get('service.cache.middleware');
        $middlewares = is_array($middlewares) ? $middlewares : [];
        $middleware = $middlewares[$method] ?? null;
        if(!is_null($middleware)){
            if(is_array($middleware)){
                //[__class__, 'static method']
                $_class = $middleware[0];
                $_method = $middleware[1] ?? null;
                if(class_exists($_class) && !empty($_method)){
                    return call_user_func_array([$_class, $_method], [$name, $value, $time, $name_prefix]);
                }
            }elseif($middleware instanceof \Closure){
                return call_user_func_array($middleware, [$name, $value, $time, $name_prefix]);
            }
        }
        return $value;
    }
}